﻿#region Description
/*  File Name:      MERenderComponent.cs
 *  Author:         Adam Goodchild
 *  
 *  Description:    A Render Component allows an Entity to render something to the Screen.
*/
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MinimizeEngine.Entities;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MinimizeEngine.Managers;
using MinimizeEngine.Collision;
#endregion

namespace MinimizeEngine.Components.Render
{
    #region Enum

    public enum PrimitiveShape
    {
        Cube,
        Sphere,
    }

    #endregion

    /// <summary>
    /// Allows an Entity to Render something to the Screen.
    /// </summary>
    public class MERenderComponent :MEBaseComponent
    {
        #region Fields

        /// <summary>
        /// Render Components Model
        /// </summary>
        private Model mModel;

        /// <summary>
        /// Scale information for the Render Component
        /// </summary>
        private Matrix mScaleMatrix;

        /// <summary>
        /// World Matrix for the Render Component
        /// </summary>
        private Matrix mWorldTransformMatrix;

        #endregion

        #region Properties

        /// <summary>
        /// The Render Components Model
        /// </summary>
        public Model Model
        {
            get { return mModel; }
        }

        /// <summary>
        /// Render Components World Transform Matrix
        /// </summary>
        public Matrix WorldTransformMatrix
        {
            get { return mWorldTransformMatrix; }
        }

        private void LoadBoundingSphereAndBox()
        {
            //Get the Custom Data from the Model when we loaded it in.
            Dictionary<string, object> modelTagData = (Dictionary<string, object>)mModel.Tag;

            //Throw an Exception if the Tag Data is null, meaning we did not store
            //the Vertex Data when loading in.
            if (modelTagData == null)
            {
                throw new InvalidOperationException("Tag Data of the Model has not been set properly." +
                                                        "Please make sure that all Models are built using the Custom Model Processor!");
            }

            //Do a quick Bounding Sphere Test to begin with.
            Parent.BoundingSphere = (BoundingSphere)modelTagData["BoundingSphere"];
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Construct a new Render COmponent
        /// </summary>
        /// <param name="parent">Entity this will be attached to</param>
        /// <param name="modelPath">Path of the Model to be loaded</param>
        public MERenderComponent(MEBaseEntity parent, string modelPath) : base(parent)
        {
            mModel = MEEngineManager.ContentManager.Load<Model>(modelPath);

            LoadBoundingSphereAndBox();

            //Add itself to the Master List of Base Components in the Scene Graph Manager
            MESceneGraphManager.AddComponent(this);
        }

        /// <summary>
        /// Construct a new Render COmponent
        /// </summary>
        /// <param name="parent">Entity this will be attached to</param>
        /// <param name="modelPath">Type of Primitive to be Loaded</param>
        public MERenderComponent(MEBaseEntity parent, PrimitiveShape shape) : base(parent)
        {
            switch(shape)
            {
                case PrimitiveShape.Cube:
                    mModel = MEEngineManager.ContentManager.Load<Model>("Content/Models/Primitives/Cube");
                    break;
                case PrimitiveShape.Sphere:
                    mModel = MEEngineManager.ContentManager.Load<Model>("Content/Models/Primitives/Sphere");
                    break;
            }

            LoadBoundingSphereAndBox();

            //Add itself to the Master List of Render Components in the Scene Graph Manager
            MESceneGraphManager.AddComponent(this);
        }

        /// <summary>
        /// Initialize the Render Component
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            mScaleMatrix = Matrix.CreateScale(Parent.Scale);
            mWorldTransformMatrix = mScaleMatrix * Parent.Rotation * Matrix.CreateTranslation(Parent.Position);
        }

        #endregion

        #region Update

        /// <summary>
        /// Update the Render Component
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            mWorldTransformMatrix = mScaleMatrix * Parent.Rotation * Matrix.CreateTranslation(Parent.Position);
            UpdateBoundingSphere();
            UpdateBoundingBox();
        }

        private void UpdateBoundingSphere()
        {
            BoundingSphere bs = Parent.BoundingSphere;
            bs.Center = Parent.Position;
            Parent.BoundingSphere = bs;
        }

        private void UpdateBoundingBox()
        {

        }

        #endregion

        #region Draw

        public override void Draw()
        {
            if (mModel != null)
            {
                MEEngineManager.DeviceManager.GraphicsDevice.RenderState.DepthBufferEnable = true;
                MEEngineManager.DeviceManager.GraphicsDevice.RenderState.AlphaBlendEnable = false;
                MEEngineManager.DeviceManager.GraphicsDevice.RenderState.AlphaTestEnable = false;

                Matrix[] transforms = new Matrix[mModel.Bones.Count];
                mModel.CopyAbsoluteBoneTransformsTo(transforms);

                foreach (ModelMesh mesh in mModel.Meshes)
                {
                    foreach (BasicEffect effect in mesh.Effects)
                    {

                        effect.EnableDefaultLighting();
                        effect.World = transforms[mesh.ParentBone.Index] * mWorldTransformMatrix;

                        effect.View = MECameraManager.mActiveCamera.Parent.View;
                        effect.Projection = MECameraManager.mActiveCamera.Parent.Projection;
                    }
                    mesh.Draw();        
                }

                if (Parent.DrawBoundingSphere)
                {
                    BoundingSphereRenderer.Render(Parent.BoundingSphere, MEEngineManager.DeviceManager.GraphicsDevice, MECameraManager.mActiveCamera.Parent.View, MECameraManager.mActiveCamera.Parent.Projection, Color.Magenta);
                }
            }
        }

        #endregion
    }
}
